<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
</body>
<script id="ColorVS" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform float u_PointSize;
    uniform mat4 u_ModelM4;
    uniform mat4 u_PvM4;
    void main(){
        gl_Position = u_PvM4 * u_ModelM4 * a_Position;
        gl_PointSize = u_PointSize;
    }
</script>
<script id="ColorFS" type="x-shader/x-fragment">
    precision mediump float;
    uniform vec4 u_Color;
    void main(){
        gl_FragColor = u_Color;
    }
</script>
<script type="module">
    import { Matrix4, Vector3, Color, OrthographicCamera, PerspectiveCamera, Spherical } from '../js/three.module.js';
    import { CreateProgram, imgPromise } from '../js/Utils.js';
    import lib from '../js/ThreeLib.js';
    import CameraController from '../js/CameraController.js'
    import Control from '../js/Control.js'
    import Square from '../js/objs/Square.js'

    import Geo from '../js/Poly2/Geo.js'
    import Mat from '../js/Poly2/Mat.js'
    import Obj3D from '../js/Poly2/Obj3D.js'
    import Scene from '../js/Poly2/Scene.js'



    //#region gl
    const canvas = document.getElementById("canvas");
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    const gl = canvas.getContext("webgl", { antialias: true, });
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);//去图片描边
    gl.enable(gl.DEPTH_TEST);
    //#endregion

    const scene = new Scene({ gl: gl });
    scene.addAxisProgram();
    scene.regProgram('color', {
        program: CreateProgram(gl,
            document.querySelector('#ColorVS').innerText,
            document.querySelector('#ColorFS').innerText),
        attribs: ['a_Position'],
        uniforms: ['u_PvM4', 'u_ModelM4', 'u_PointSize', 'u_Color']
    })


    const pvM4 = new Matrix4();
    const modelM4 = new Matrix4();
    const eye = new Vector3(0, 0, 3);
    const lookAt = new Vector3();

    console.log(canvas.width / canvas.height)
    const camera = new PerspectiveCamera(45, canvas.width / canvas.height, 1, 20);
    const cameraController = new CameraController({
        canvas,
        camera,
        pvMatrix: pvM4,
        eye,
        lookAt,
    });

    const axis = new Obj3D().createAxis('axis');
    scene.add(axis);


    //#region main
    //这里是鼠标对象
    let obj01 = create([0, 0, 0], null, [1, 1, 0, 1])
    scene.add(obj01);

    const objArr = [];
    //这是需要检测的Obj对象
    const nodeLength = 0.5;
    let obj02 = create([
        -nodeLength, nodeLength, 0,
        -nodeLength, -nodeLength, 0,
        nodeLength, -nodeLength, 0,
        nodeLength, nodeLength, 0,
    ], [
        0, 1, 2,
        0, 2, 3,
    ], [1, 1, 1, 1]);
    scene.add(obj02);
    objArr.push(obj02);


    canvas.addEventListener('mousemove', function (event) {
        const { clientX, clientY } = event;

        const curPosi = getViewToWorld(clientX, clientY);
        obj01.setAttrib('a_Position', { array: new Float32Array([curPosi.x, curPosi.y, curPosi.z]) })

        //判断鼠标是否进入了某个图形
        for (let i = 0; i < objArr.length; i++) {
            const element = objArr[i];
            const triangle = getTriangle(element);

            for (let p = 0; p < triangle.length; p++) {
                const m = toFaceNode(eye, curPosi, triangle[p]);

                const isIn = inTriangle(m, triangle[p]);

                if (isIn) {
                    console.log(isIn);
                    return;
                }
            }
        }
    });
    //#endregion

    //#region  检测
    //获取obj对象的三角形集合
    function getTriangle(obj3D) {
        const element = obj3D.geo.element.array;
        const node = obj3D.geo.data['a_Position'].array;

        const triangles = [];
        const modelM4 = new Matrix4();
        modelM4.elements = obj3D.mat.data['u_ModelM4'].value;

        for (let i = 0; i < element.length; i += 3) {
            const a = new Vector3(
                node[element[i] * 3 + 0],
                node[element[i] * 3 + 1],
                node[element[i] * 3 + 2],
            ).applyMatrix4(modelM4);
            const b = new Vector3(
                node[element[i + 1] * 3 + 0],
                node[element[i + 1] * 3 + 1],
                node[element[i + 1] * 3 + 2],
            ).applyMatrix4(modelM4);
            const c = new Vector3(
                node[element[i + 2] * 3 + 0],
                node[element[i + 2] * 3 + 1],
                node[element[i + 2] * 3 + 2],
            ).applyMatrix4(modelM4);
            triangles.push([a, b, c]);
        }
        return triangles;
    }
    //获取射线到面上的点
    function toFaceNode(eye, lookAt, nodeArr) {
        if (nodeArr.length < 3) {
            return;
        }
        const v = lookAt.clone().sub(eye);
        const n = nodeArr[1].clone().sub(nodeArr[0]).cross(nodeArr[2].clone().sub(nodeArr[1]));
        const m = v.clone().multiplyScalar(nodeArr[0].clone().sub(eye).dot(n) / v.clone().dot(n)).add(eye);
        return m;
    }
    //获取点是否在三角面上
    function inTriangle(m, nodeArr) {
        if (lib.inTriangle3(m, nodeArr)) {
            return true;
        }
        return false;
    }
    //#endregion

    (function ani() {
        scene.allTransform();

        draw();
        requestAnimationFrame(ani)
    })();

    function getViewToWorld(clientX, clientY) {
        const [hx, hy] = [canvas.width / 2, canvas.height / 2];
        const [x, y] = [(clientX - hx) / hx, -(clientY - hy) / hy];
        const worldPosi = new Vector3(x, y, 0).applyMatrix4(pvM4.clone().invert())
        return worldPosi;
    }

    function cssToGl(x, y) {
        const [hx, hy] = [canvas.width / 2, canvas.height / 2];
        //裁剪空间位, 及屏幕位
        const cp = new Vector3((x - hx) / hx, -(y - hy) / hy, 0);
        //世界坐标位置, 即裁剪空间位 * 投影矩阵的逆坐标
        const p = cp.applyMatrix4(pvM4.clone().invert());
        return p;
    }

    function create(vertices, elements = null, color = [Math.random(), Math.random(), Math.random(), 1]) {
        const geo = new Geo({
            data: {
                'a_Position': { size: 3, array: new Float32Array(vertices) },
            }
        });
        const mat = new Mat({
            program: 'color',
            mode: ['LINES', 'POINTS'],
            data: {
                'u_PvM4': { type: 'uniformMatrix4fv', value: pvM4.elements },
                'u_ModelM4': { type: 'uniformMatrix4fv', value: modelM4.elements },
                'u_PointSize': { type: 'uniform1f', value: 30 },
                'u_Color': { type: 'uniform4fv', value: color },
            },
        });
        if (elements != null) {
            geo.element = { array: new Uint16Array(elements) };
            mat.mode = ['TRIANGLES'];
        }
        return new Obj3D({ geo: geo, mat: mat, });
    }

    function draw() {
        scene.setUniform('u_PvM4', { value: pvM4.elements });
        scene.draw();
    }
</script>

</html>